import { ViewportState, SceneBlockData, DragState } from './types'

/**
 * 时间线Canvas渲染器
 * 负责绘制时间线的所有视觉元素
 */
export class TimelineRenderer {
  private canvas: HTMLCanvasElement
  private ctx: CanvasRenderingContext2D

  constructor(canvas: HTMLCanvasElement) {
    this.canvas = canvas
    const context = canvas.getContext('2d')
    if (!context) {
      throw new Error('无法获取Canvas 2D渲染上下文')
    }
    this.ctx = context
    this.setupCanvas()
  }

  /**
   * 设置Canvas像素比和尺寸
   */
  private setupCanvas(): void {
    const dpr = window.devicePixelRatio || 1
    const rect = this.canvas.getBoundingClientRect()
    
    this.canvas.width = rect.width * dpr
    this.canvas.height = rect.height * dpr
    
    this.ctx.scale(dpr, dpr)
    this.canvas.style.width = `${rect.width}px`
    this.canvas.style.height = `${rect.height}px`
  }

  /**
   * 更新Canvas尺寸
   */
  updateSize(): void {
    this.setupCanvas()
  }

  /**
   * 清空Canvas
   */
  clear(): void {
    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
  }

  /**
   * 主渲染方法
   */
  render(
    viewport: ViewportState,
    sceneBlocks: SceneBlockData[],
    timeMarks: Array<{x: number; time: number; label: string; isPrimary: boolean}>,
    dragState: DragState,
    selectedSceneId: string | null
  ): void {
    this.clear()
    this.drawBackground(viewport)
    this.drawTimeScale(timeMarks)
    this.drawSceneBlocks(sceneBlocks, selectedSceneId, dragState)
    
    if (dragState.isDragging) {
      this.drawDragIndicator(dragState, viewport)
    }
  }

  /**
   * 绘制背景
   */
  private drawBackground(viewport: ViewportState): void {
    // 主背景
    this.ctx.fillStyle = '#fafafa'
    this.ctx.fillRect(0, 0, viewport.width, viewport.height)
    
    // 时间刻度区域背景
    this.ctx.fillStyle = '#f0f0f0'
    this.ctx.fillRect(0, 0, viewport.width, 20)
    
    // 分隔线
    this.ctx.strokeStyle = '#e8e8e8'
    this.ctx.lineWidth = 1
    this.ctx.beginPath()
    this.ctx.moveTo(0, 20)
    this.ctx.lineTo(viewport.width, 20)
    this.ctx.stroke()
  }

  /**
   * 绘制时间刻度
   */
  private drawTimeScale(timeMarks: Array<{x: number; time: number; label: string; isPrimary: boolean}>): void {
    this.ctx.font = '11px -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif'
    this.ctx.textAlign = 'center'
    this.ctx.textBaseline = 'middle'

    timeMarks.forEach(mark => {
      // 刻度线
      this.ctx.strokeStyle = mark.isPrimary ? '#666' : '#ccc'
      this.ctx.lineWidth = 1
      this.ctx.beginPath()
      this.ctx.moveTo(mark.x, mark.isPrimary ? 15 : 17)
      this.ctx.lineTo(mark.x, 20)
      this.ctx.stroke()

      // 刻度标签
      if (mark.isPrimary) {
        this.ctx.fillStyle = '#666'
        this.ctx.fillText(mark.label, mark.x, 10)
      }
    })
  }

  /**
   * 绘制场景块
   */
  private drawSceneBlocks(
    sceneBlocks: SceneBlockData[], 
    selectedSceneId: string | null, 
    dragState: DragState
  ): void {
    sceneBlocks.forEach(block => {
      const isSelected = block.scene.id === selectedSceneId
      const isDragged = block.scene.id === dragState.draggedSceneId
      
      if (isDragged && dragState.isDragging) {
        // 被拖拽的场景块以半透明显示
        this.drawSceneBlock(block, isSelected, 0.5)
      } else {
        this.drawSceneBlock(block, isSelected, 1.0)
      }
    })
  }

  /**
   * 绘制单个场景块
   */
  private drawSceneBlock(block: SceneBlockData, isSelected: boolean, opacity: number): void {
    const { x, y, width, height, scene } = block
    
    this.ctx.save()
    this.ctx.globalAlpha = opacity

    // 场景块背景
    const baseColor = this.getSceneColor(scene)
    this.ctx.fillStyle = isSelected ? this.lightenColor(baseColor, 0.2) : baseColor
    this.ctx.fillRect(x, y, width, height)

    // 场景块边框
    this.ctx.strokeStyle = isSelected ? '#1890ff' : '#d9d9d9'
    this.ctx.lineWidth = isSelected ? 2 : 1
    this.ctx.strokeRect(x, y, width, height)

    // 场景标题
    this.ctx.fillStyle = '#333'
    this.ctx.font = '12px -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif'
    this.ctx.textAlign = 'left'
    this.ctx.textBaseline = 'middle'
    
    // 文本裁剪
    const maxTextWidth = width - 8
    const text = this.truncateText(scene.title, maxTextWidth)
    this.ctx.fillText(text, x + 4, y + height / 2)

    this.ctx.restore()
  }

  /**
   * 绘制拖拽指示器
   */
  private drawDragIndicator(dragState: DragState, viewport: ViewportState): void {
    if (dragState.insertIndex < 0) return

    // 绘制插入位置的垂直线
    this.ctx.strokeStyle = '#ff4d4f'
    this.ctx.lineWidth = 2
    this.ctx.setLineDash([4, 4])
    
    this.ctx.beginPath()
    this.ctx.moveTo(dragState.dragCurrentX, 20)
    this.ctx.lineTo(dragState.dragCurrentX, viewport.height)
    this.ctx.stroke()
    
    this.ctx.setLineDash([])
  }

  /**
   * 根据场景类型获取颜色
   */
  private getSceneColor(scene: any): string {
    // 根据场景的镜头类型或其他属性确定颜色
    const colors = [
      '#e6f4ff', // 浅蓝
      '#f6ffed', // 浅绿
      '#fff2e8', // 浅橙
      '#f9f0ff', // 浅紫
      '#fff1f0', // 浅红
    ]
    
    // 简单的哈希方法确定颜色
    const hash = scene.id.split('').reduce((a, b) => {
      a = ((a << 5) - a) + b.charCodeAt(0)
      return a & a
    }, 0)
    
    return colors[Math.abs(hash) % colors.length]
  }

  /**
   * 颜色亮度调整
   */
  private lightenColor(color: string, amount: number): string {
    const usePound = color[0] === '#'
    const col = usePound ? color.slice(1) : color
    
    const num = parseInt(col, 16)
    let r = (num >> 16) + amount * 255
    let g = (num >> 8 & 0x00FF) + amount * 255
    let b = (num & 0x0000FF) + amount * 255
    
    r = r > 255 ? 255 : r < 0 ? 0 : r
    g = g > 255 ? 255 : g < 0 ? 0 : g
    b = b > 255 ? 255 : b < 0 ? 0 : b
    
    return (usePound ? '#' : '') + (r << 16 | g << 8 | b).toString(16).padStart(6, '0')
  }

  /**
   * 文本截断
   */
  private truncateText(text: string, maxWidth: number): string {
    const metrics = this.ctx.measureText(text)
    if (metrics.width <= maxWidth) {
      return text
    }
    
    // 逐渐缩短文本直到适合
    for (let i = text.length - 1; i > 0; i--) {
      const truncated = text.slice(0, i) + '...'
      const truncatedMetrics = this.ctx.measureText(truncated)
      if (truncatedMetrics.width <= maxWidth) {
        return truncated
      }
    }
    
    return '...'
  }
}